package com.igroupes.ovineherd.service.impl;

import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.ReflectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.igroupes.ovineherd.constant.Constants;
import com.igroupes.ovineherd.constant.FieldConstant;
import com.igroupes.ovineherd.dto.request.GetRequestAttribute;
import com.igroupes.ovineherd.dto.request.ModelFieldBatchRequest;
import com.igroupes.ovineherd.dto.request.ModelFieldRequest;
import com.igroupes.ovineherd.dto.response.ModelDataListPageResponse;
import com.igroupes.ovineherd.dto.response.ModelDataListResponse;
import com.igroupes.ovineherd.entity.DataModelAttributeEntity;
import com.igroupes.ovineherd.entity.DataModelTableEntity;
import com.igroupes.ovineherd.enums.BooleanKey;
import com.igroupes.ovineherd.enums.ErrorCode;
import com.igroupes.ovineherd.exception.ModelException;
import com.igroupes.ovineherd.exception.OvineHerdException;
import com.igroupes.ovineherd.model.RelGetRequestAttribute;
import com.igroupes.ovineherd.model.RelGetRequestWhereNode;
import com.igroupes.ovineherd.model.attribute.BasicAttribute;
import com.igroupes.ovineherd.model.dto.FieldApiResponse;
import com.igroupes.ovineherd.model.enums.BeanTypeKey;
import com.igroupes.ovineherd.model.enums.MySqlTypeKey;
import com.igroupes.ovineherd.model.enums.SqlFieldKey;
import com.igroupes.ovineherd.raw.service.raw.DataModelAttributeService;
import com.igroupes.ovineherd.raw.service.raw.DataModelTableService;
import com.igroupes.ovineherd.raw.service.raw.DataModelVersionService;
import com.igroupes.ovineherd.service.ModelApiService;
import com.igroupes.ovineherd.util.DateUtils;
import com.igroupes.ovineherd.util.Requires;
import com.igroupes.ovineherd.util.SQLUtils;
import com.igroupes.ovineherd.vo.ResultVO;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.jdbc.SQL;
import org.assertj.core.util.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import sun.rmi.runtime.Log;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import java.util.stream.Collectors;

import static com.igroupes.ovineherd.constant.Constants.CNT_COLUMN;
import static com.igroupes.ovineherd.enums.ErrorCode.ADD_FAIL;
import static com.igroupes.ovineherd.enums.ErrorCode.SYSTEM_ERROR;

@Service
@Slf4j
public class ModelApiServiceImpl implements ModelApiService {
    @Autowired
    private DataModelTableService modelTableService;
    @Autowired
    private DataModelAttributeService modelAttributeService;
    @Autowired
    private DataModelVersionService dataModelVersionService;
    @Autowired
    private DataSource dataSource;


    /**
     * 添加数据
     *
     * @param modelId
     * @return
     */
    @Override
    public ResultVO add(Long modelId, ModelFieldRequest request, Long userId) {
        DataModelTableEntity modelTableEntity = checkModelId(modelId);
        if (CollectionUtils.isEmpty(request.getFields())) {
            return ResultVO.success();
        }
        // 传入的参数校验
        List<FieldApiResponse> fieldApiResponses = checkArg(modelId, request, userId);
        addBaseFields(modelTableEntity, fieldApiResponses, true);
        Requires.requireTrue(fieldApiResponses.size() != 0, "field api response list is empty");
        UpsertDataModel realDataModel = getUpsertDataModel(modelId, fieldApiResponses);
        Requires.requireNonNull(realDataModel, String.format("modelId : %s , args : %s parse fail ", modelId, request));
        checkUpsertDataModel(realDataModel);

        SQLUtils.transactionCommit(() -> dataSource.getConnection(),
                connection -> {
                    PreparedStatement preparedStatement = upsertDataModelToSatement(modelTableEntity, realDataModel, null, true, connection);
                    int cnt = preparedStatement.executeUpdate();
                    if (cnt < 0) {
                        throw new OvineHerdException(ADD_FAIL);
                    }
                });
        return ResultVO.success();
    }

    /**
     * 添加基础字段
     *
     * @param fieldApiResponses
     */
    private void addBaseFields(DataModelTableEntity modelTableEntity, List<FieldApiResponse> fieldApiResponses, boolean add) {
        Requires.requireNonNull(modelTableEntity, "dataModelTableEntity is null");
        boolean addDateTime = modelTableEntity.getAddDatetime().equals(BooleanKey.TRUE.getCode());
        boolean updateDateTime = modelTableEntity.getEditDatetime().equals(BooleanKey.TRUE.getCode());
        boolean softdel = modelTableEntity.getSoftDel().equals(BooleanKey.TRUE.getCode());
        if (!addDateTime && !updateDateTime) {
            return;
        }
        String nowDateTime = DateUtils.getDateTime();
        if (addDateTime && add) {
            FieldApiResponse fieldApiResponse = new FieldApiResponse();
            fieldApiResponse.setDbName(SqlFieldKey.ADDTIME.getKey());
            fieldApiResponse.setIsStr(true);
            fieldApiResponse.setValue(nowDateTime);
            fieldApiResponse.setType(MySqlTypeKey.VARCHAR.getCode());
            fieldApiResponses.add(fieldApiResponse);

            if (softdel) {
                FieldApiResponse fieldApiResponse2 = new FieldApiResponse();
                fieldApiResponse2.setDbName(SqlFieldKey.ISDEL.getKey());
                fieldApiResponse2.setIsStr(false);
                fieldApiResponse2.setValue(0);
                fieldApiResponse2.setType(MySqlTypeKey.BIGINT.getCode());
                fieldApiResponses.add(fieldApiResponse2);
            }
        }

        if (updateDateTime) {
            FieldApiResponse fieldApiResponse = new FieldApiResponse();
            fieldApiResponse.setDbName(SqlFieldKey.EDITTIME.getKey());
            fieldApiResponse.setIsStr(true);
            fieldApiResponse.setValue(nowDateTime);
            fieldApiResponse.setType(MySqlTypeKey.VARCHAR.getCode());
            fieldApiResponses.add(fieldApiResponse);
        }
    }


    @Override
    public ResultVO update(Long modelId, Long id, ModelFieldRequest request, Long userId) {
        return updateBatch(modelId, Lists.newArrayList(id.toString()), request, userId);
    }

    public ResultVO updateBatch(Long modelId, List<String> ids, ModelFieldRequest request, Long userId) {
        DataModelTableEntity modelTableEntity = checkModelId(modelId);
        if (CollectionUtils.isEmpty(request.getFields())) {
            return ResultVO.success();
        }
        // 传入的参数校验
        List<FieldApiResponse> fieldApiResponses = checkArg(modelId, request, userId);
        addBaseFields(modelTableEntity, fieldApiResponses, false);
        Requires.requireTrue(fieldApiResponses.size() != 0, "field api response list is empty");
        UpsertDataModel realDataModel = getUpsertDataModel(modelId, fieldApiResponses);
        Requires.requireNonNull(realDataModel, String.format("modelId : %s , args : %s parse fail ", modelId, request));
        checkUpsertDataModel(realDataModel);
        SQLUtils.transactionCommit(() -> dataSource.getConnection(),
                connection -> {
                    PreparedStatement preparedStatement = upsertDataModelToSatement(modelTableEntity, realDataModel, ids, false, connection);
                    int[] cnt = preparedStatement.executeBatch();
                    log.debug("modelId:{} , ids :{} , args :{} , update {} rows", modelId, ids, request, Arrays.toString(cnt));
                });
        return ResultVO.success();
    }


    @Override
    public ResultVO delete(Long modelId, Long id) {
        return delete(modelId, Lists.newArrayList(id.toString()));
    }

    private ResultVO delete(Long modelId, List<String> ids) {
        DataModelTableEntity modelTableEntity = checkModelId(modelId);
        DatabaseSchema databaseSchema = getDatabaseSchema(modelId);
        Requires.requireNonNull(databaseSchema, String.format("database schema is null by model id : %s ", modelId));
        SQLUtils.transactionCommit(() -> dataSource.getConnection(),
                connection -> {
                    PreparedStatement preparedStatement = getDeletePreparedStatement(modelTableEntity, databaseSchema, ids, connection);
                    int[] ret = preparedStatement.executeBatch();
                    log.debug("modelId:{} , id :{}  , delete {} rows", modelId, ids, Arrays.toString(ret));
                });
        return ResultVO.success();
    }


    private PreparedStatement getDeletePreparedStatement(DataModelTableEntity modelTableEntity, DatabaseSchema databaseSchema, List<String> ids, Connection connection) throws SQLException {
        String sql = null;
        PreparedStatement preparedStatement = null;
        if (modelTableEntity.getSoftDel().equals(BooleanKey.TRUE.getCode())) {
            SQL sqlStatement = new SQL().UPDATE(databaseSchema.getDatabaseName() + "." + databaseSchema.getTableName())
                    .SET(SqlFieldKey.ISDEL.getKey() + "=?");
            boolean updateDatetime = modelTableEntity.getEditDatetime().equals(BooleanKey.TRUE.getCode());
            if (updateDatetime) {
                sqlStatement.SET(SqlFieldKey.EDITTIME.getKey() + "=?");
            }
            sqlStatement.WHERE("id = ?").AND().WHERE(SqlFieldKey.ISDEL.getKey() + "=0");
            sql = sqlStatement.toString();
            preparedStatement = connection.prepareStatement(sql);
            for (String id : ids) {
                preparedStatement.setLong(1, System.currentTimeMillis());
                if (updateDatetime) {
                    preparedStatement.setString(2, DateUtils.getDateTime());
                    preparedStatement.setString(3, id);
                } else {
                    preparedStatement.setString(2, id);
                }
                preparedStatement.addBatch();
            }
        } else {
            sql = new SQL().DELETE_FROM(databaseSchema.getDatabaseName() + "." + databaseSchema.getTableName())
                    .WHERE("id = ?").toString();
            preparedStatement = connection.prepareStatement(sql);
            for (String id : ids) {
                preparedStatement.setString(1, id);
                preparedStatement.addBatch();
            }
        }
        log.debug("delete op sql : {}", sql);
        return preparedStatement;
    }

    @Override
    public ResultVO get(Long modelId, GetRequestAttribute attribute) {
        RelGetRequestAttribute relGetRequestAttribute = new RelGetRequestAttribute();
        relGetRequestAttribute.byGetRequestAttribute(getFieldMapByModelId(modelId), this::getFieldInfoByFieldMap, attribute);
        GetPageSQL requestToSQL = getRequestSQL(modelId, relGetRequestAttribute);
        ModelDataListResponse response = new ModelDataListResponse();
        List<Map<String, Object>> data = null;
        if (requestToSQL.getSql() != null) {
            data = SQLUtils.queryToList(() -> dataSource.getConnection(), requestToSQL.getSql(), requestToSQL.getSqlPlaceHolder());
        }
        data = transformLabel(modelId, data);
        response.setData(data);
        if (requestToSQL.getTotalSQL() != null) {
            ModelDataListPageResponse pageResponse = new ModelDataListPageResponse();
            pageResponse.setData(data);
            pageResponse.setPage(attribute.getPaging().getPage());
            pageResponse.setSize(data.size());
            SQLUtils.commitWithoutTransaction(() -> dataSource.getConnection(), conn -> {
                PreparedStatement preparedStatement = conn.prepareStatement(requestToSQL.getTotalSQL());
                for (int i = 1; i <= requestToSQL.getTotalPlaceHolder().size(); i++) {
                    preparedStatement.setObject(i, requestToSQL.getTotalPlaceHolder().get(i - 1));
                }
                ResultSet resultSet = preparedStatement.executeQuery();
                int cnt = 0;
                while (resultSet.next()) {
                    pageResponse.setCount(resultSet.getLong(CNT_COLUMN));
                    cnt++;
                    if (cnt > 1) {
                        log.error("sql:{} result set rather than 1 row", requestToSQL.getTotalSQL());
                        throw new OvineHerdException(ErrorCode.SYSTEM_ERROR);
                    }
                }
            });
            return ResultVO.success(pageResponse);
        } else {
            return ResultVO.success(response);
        }
    }

    @Override
    public ResultVO getOne(Long modelId, Long id) {
        DataModelTableEntity modelTableEntity = checkModelId(modelId);
        DatabaseSchema databaseSchema = getDatabaseSchema(modelId);
        SQL sql = new SQL().SELECT("*")
                .FROM(String.format("%s.%s", databaseSchema.getDatabaseName(), databaseSchema.getTableName()))
                .WHERE("id = ?");
        if (isSoftDel(modelTableEntity)) {
            sql.AND().WHERE(SqlFieldKey.ISDEL.getKey() + "=0");
        }
        List<Map<String, Object>> data = SQLUtils.queryToList(() -> dataSource.getConnection(), sql.toString(), Lists.newArrayList(id));
        transformLabel(modelId, data);
        if (data.size() == 0) {
            return ResultVO.success();
        } else {
            return ResultVO.success(data.get(0));
        }
    }

    @Override
    public ResultVO deleteBatch(Long modelId, List<String> ids) {
        return delete(modelId, ids);
    }

    @Override
    public ResultVO updateBatch(Long modelId, ModelFieldBatchRequest request, Long userId) {
        return updateBatch(modelId, Splitter.on(Constants.COMMA).trimResults().splitToList(request.getIds()), request, userId);
    }

    /**
     * 正常返回的是字段，需要修改成描述
     *
     * @param data
     * @return
     */
    private List<Map<String, Object>> transformLabel(Long modelId, List<Map<String, Object>> data) {
        clearInterFields(data);
        QueryWrapper<DataModelAttributeEntity> wrapper = new QueryWrapper<>();

        wrapper.lambda().eq(DataModelAttributeEntity::getTableId, modelId);

        Map<String, DataModelAttributeEntity> fieldIdMap = modelAttributeService.list(wrapper).stream().collect(Collectors.toMap(DataModelAttributeEntity::getDbName, e -> e));

        // 某一些字段类型，结果数据需要修改，比如：枚举类型，数据库中是字符串类型但是返回的是列表
        updateResultData(fieldIdMap, data);
        List<Map<String, Object>> ret = new ArrayList<>();
        // TODO 目前生成的基础字段是：add_datetime，前端一般使用的是addTime,建议改成add_time. 其他类推
        List<String> baseKeyList = SqlFieldKey.baseFieldNameList();
        for (Map<String, Object> row : data) {
            Map<String, Object> one = new HashMap<>();
            for (Map.Entry<String, Object> entry : row.entrySet()) {
                if (!baseKeyList.contains(entry.getKey())) {
                    if (fieldIdMap.containsKey(entry.getKey())) {
                        one.put(fieldIdMap.get(entry.getKey()).getId().toString(), entry.getValue());
                    }
                } else {
                    if (FieldConstant.SHOW_MAP.containsKey(entry.getKey())) {
                        one.put(FieldConstant.SHOW_MAP.get(entry.getKey()), entry.getValue());
                    } else {
                        one.put(entry.getKey(), entry.getValue());
                    }
                }
            }
            ret.add(one);
        }
        return ret;
    }

    private void updateResultData(Map<String, DataModelAttributeEntity> fieldIdMap, List<Map<String, Object>> data) {
        for (Map<String, Object> row : data) {
            for (String col : row.keySet()) {
                // 基础字段特殊处理
                if (!SqlFieldKey.baseFieldNameList().contains(col)) {
                    DataModelAttributeEntity dataModelAttributeEntity = fieldIdMap.get(col);
                    if (dataModelAttributeEntity == null) {
                        log.error("字段：{}没有找到", col);
                        throw new OvineHerdException(SYSTEM_ERROR);
                    }
                    Object newData = getByBeanType(dataModelAttributeEntity.getBeanType()).getData(dataModelAttributeEntity, row.get(col));
                    row.put(col, newData);
                }
            }
        }
    }

    private void clearInterFields(List<Map<String, Object>> data) {
        if (CollectionUtils.isEmpty(data)) {
            return;
        }
        final String[] interFields = {"version", SqlFieldKey.ISDEL.getKey()};
        for (Map<String, Object> row : data) {
            for (String s : interFields) {
                row.remove(s);
            }
        }
    }


    private DatabaseSchema getDatabaseSchema(Long modelId) {
        DataModelTableEntity dataModelTableEntity = modelTableService.getById(modelId);
        String[] dbtableName = StringUtils.split(dataModelTableEntity.getDbName(), ".");
        Requires.requireTrue(dbtableName.length == 2, "field dbname is dbname.dbtable");
        return new DatabaseSchema(dbtableName[0].trim(), dbtableName[1].trim());
    }

    private GetPageSQL getRequestSQL(Long modelId, RelGetRequestAttribute relGetRequestAttribute) {
        DatabaseSchema databaseSchema = getDatabaseSchema(modelId);
        String totalSQL = null;
        List<Object> sqlPlaceHolder = new ArrayList<>();
        boolean isPage = relGetRequestAttribute.getPaging() != null;
        SQL sqlStatement = new SQL().SELECT("*")
                .FROM(String.format("%s.%s", databaseSchema.getDatabaseName(), databaseSchema.getTableName()));
        if (isSoftDel(modelId)) {
            sqlStatement.WHERE(SqlFieldKey.ISDEL.getKey() + "=0");
        }

        StringBuilder builder = new StringBuilder();
        setRequestSQL(builder, relGetRequestAttribute.getConditions(), sqlPlaceHolder);
        if (builder.length() != 0) {
            sqlStatement.AND().WHERE(builder.toString());
        }
        List<Object> totalPlaceHolder = null;
        if (isPage) {
            totalPlaceHolder = new ArrayList<>(sqlPlaceHolder);
            totalSQL = new SQL().SELECT("count(1) " + CNT_COLUMN).FROM("(" + sqlStatement.toString() + ") t").toString();
            log.debug("total sql :{} ", totalSQL);
        }
        if (relGetRequestAttribute.getOrdering() != null) {
            for (RelGetRequestAttribute.Ordering ordering : relGetRequestAttribute.getOrdering()) {
                sqlStatement.ORDER_BY(ordering.getFieldName() + (ordering.isAsc() ? " asc " : " desc "));
            }
        }
        if (isPage) {
            RelGetRequestAttribute.Paging paging = relGetRequestAttribute.getPaging();
            sqlStatement.OFFSET((paging.getPage() - 1) * paging.getSize());
            sqlStatement.LIMIT(paging.getSize());
        }
        String sql = sqlStatement.toString();
        log.debug("get sql: {}", sql);
        return new GetPageSQL(sql, sqlPlaceHolder, totalSQL, totalPlaceHolder);
    }


    private void setRequestSQL(StringBuilder builder, RelGetRequestWhereNode node, List<Object> sqlPlaceHolder) {
        if (node == null) {
            return;
        }
        RelGetRequestWhereNode.ConnRelation relation = node.getConjunction();
        if (CollectionUtils.isNotEmpty(node.getChildren())) {
            int i = 0;
            for (; i < node.getChildren().size(); i++) {
                toSQL(builder, node.getChildren().get(i), sqlPlaceHolder);
                if (i < node.getChildren().size() - 1) {
                    relationToSQL(builder, relation);
                }
            }
        }
    }


    private StringBuilder relationToSQL(StringBuilder builder, RelGetRequestWhereNode.ConnRelation relation) {
        switch (relation) {
            case AND:
                return builder.append(" and ");
            case OR:
                return builder.append(" or ");
            default:
                throw new UnsupportedOperationException();
        }
    }

    private void toSQL(StringBuilder builder, RelGetRequestWhereNode.FieldWithValue fieldWithValue, List<Object> sqlPlaceHolder) {
        RelGetRequestWhereNode.OpType op = fieldWithValue.getOp();

        if (op != null) {
            builder.append("(").append(fieldWithValue.getFieldName()).append(" ");
            if (op == RelGetRequestWhereNode.OpType.BETWEEN || op == RelGetRequestWhereNode.OpType.NOT_BETWEEN) {
                Requires.requireTrue(fieldWithValue.getRight() instanceof List);
                List range = (List) fieldWithValue.getRight();
                Requires.requireTrue(range.size() == 2, "list size must 2");
                if (op == RelGetRequestWhereNode.OpType.BETWEEN) {
                    builder.append("between ? and ? ");
                } else {
                    builder.append("not between ? and ? ");
                }
                builder.append(")");
                sqlPlaceHolder.add(range.get(0));
                sqlPlaceHolder.add(range.get(1));
                return;
            }
            if (op != RelGetRequestWhereNode.OpType.IS_EMPTY && op != RelGetRequestWhereNode.OpType.IS_NOT_EMPTY) {
                sqlPlaceHolder.add(fieldWithValue.getRight());
            }
            switch (op) {
                case LESS:
                    builder.append("<?");
                    break;
                case LIKE:
                    builder.append("like concat(\"%\",?,\"%\")");
                    break;
                case EQUAL:
                    builder.append("=?");
                    break;
                case GREATER:
                    builder.append(">?");
                    break;
                case IS_EMPTY:
                    builder.append("? is null");
                    break;
                case NOT_LIKE:
                    builder.append("not like concat(\"%\",?,\"%\")");
                    break;
                case ENDS_WITH:
                    builder.append("like concat(\"%\",?)");
                    break;
                case NOT_EQUAL:
                    builder.append("!=?");
                    break;
                case STARTS_WITH:
                    builder.append("like concat(?,\"%\")");
                    break;
                case IS_NOT_EMPTY:
                    builder.append("is not null");
                    break;
                case LESS_OR_EQUAL:
                    builder.append("<=?");
                    break;
                case GREATER_OR_EQUAL:
                    builder.append(">=?");
                    break;
                default:
                    throw new UnsupportedOperationException();
            }
            builder.append(")");
            return;
        }
        List<RelGetRequestWhereNode.FieldWithValue> children = fieldWithValue.getChildren();
        if (CollectionUtils.isNotEmpty(children)) {
            int i = 0;
            for (; i < children.size(); i++) {
                toSQL(builder, children.get(i), sqlPlaceHolder);
                if (i < children.size() - 1) {
                    relationToSQL(builder, fieldWithValue.getConjunction());
                }
            }
        }
    }


    @Data
    @AllArgsConstructor
    class GetPageSQL {
        private String sql; //正常的sql,包含分页
        private List<Object> sqlPlaceHolder;
        private String totalSQL;  // 只有存在分页的情况下在不为空，表示计算总条数的SQL
        private List<Object> totalPlaceHolder;
    }


    @Data
    @AllArgsConstructor
    class DatabaseSchema {
        private String databaseName;
        private String tableName;
    }

    /**
     * realDataModel convert to SQL
     *
     * @param realDataModel
     * @param insert        if true  insert , else  update
     * @return
     */
    private PreparedStatement upsertDataModelToSatement(DataModelTableEntity modelTableEntity, UpsertDataModel realDataModel, List<String> ids, boolean insert, Connection connection) throws SQLException {
        PreparedStatement preparedStatement = null;
        if (insert) {
            String fieldNames = Joiner.on(",").join(realDataModel.getFieldInfoList().stream().map(UpsertDataModel.FieldInfo::getFieldName).collect(Collectors.toList()));
            int fieldSize = realDataModel.getFieldInfoList().size();
            StringBuilder builder = new StringBuilder();
            for (int i = 0; i < fieldSize; i++) {
                builder.append("?").append(",");
            }
            String placeHolder = builder.substring(0, builder.length() - 1);
            String sql = String.format("insert into  %s.%s (%s) values (%s)"
                    , realDataModel.getDatabaseName(), realDataModel.getTableName(), fieldNames, placeHolder);
            log.debug("sql : {}", sql);
            preparedStatement = connection.prepareStatement(sql);
            for (int i = 1; i <= fieldSize; i++) {
                UpsertDataModel.FieldInfo fieldInfo = realDataModel.getFieldInfoList().get(i - 1);
                if (fieldInfo.isStr) {
                    preparedStatement.setString(i, fieldInfo.getValue().toString());
                } else {
                    preparedStatement.setObject(i, fieldInfo.getValue());
                }
            }

        } else {
            StringBuilder builder = new StringBuilder();
            for (UpsertDataModel.FieldInfo fieldInfo : realDataModel.getFieldInfoList()) {
                builder.append(fieldInfo.fieldName).append("=?,");
            }
            String sql = null;
            if (isSoftDel(modelTableEntity)) {
                sql = String.format("update  %s.%s set %s where id = ? and " + SqlFieldKey.ISDEL.getKey() + "=0",
                        realDataModel.getDatabaseName(), realDataModel.getTableName(), builder.substring(0, builder.length() - 1));
            } else {
                sql = String.format("update  %s.%s set %s where id = ? ",
                        realDataModel.getDatabaseName(), realDataModel.getTableName(), builder.substring(0, builder.length() - 1));
            }

            log.debug("sql : {}", sql);
            preparedStatement = connection.prepareStatement(sql);
            for (String id : ids) {
                int i = 1;
                for (; i <= realDataModel.getFieldInfoList().size(); i++) {
                    UpsertDataModel.FieldInfo fieldInfo = realDataModel.getFieldInfoList().get(i - 1);
                    if (fieldInfo.isStr || fieldInfo.getValue().getClass() == String.class) {
                        preparedStatement.setString(i, fieldInfo.getValue().toString());
                    } else {
                        preparedStatement.setObject(i, fieldInfo.getValue());
                    }
                }
                preparedStatement.setString(i, id);
                preparedStatement.addBatch();
            }
        }
        return preparedStatement;
    }

    private boolean isSoftDel(Long modelId) {
        DataModelTableEntity modelTableEntity = checkModelId(modelId);
        return isSoftDel(modelTableEntity);
    }

    private boolean isSoftDel(DataModelTableEntity modelTableEntity) {
        return modelTableEntity.getSoftDel().equals(BooleanKey.TRUE.getCode());
    }

    private void checkUpsertDataModel(UpsertDataModel realDataModel) {
        Requires.requireNonBlank(realDataModel.getDatabaseName(), "database name not found");
        Requires.requireNonBlank(realDataModel.getTableName(), "table name not found");
        for (UpsertDataModel.FieldInfo fieldInfo : realDataModel.getFieldInfoList()) {
            // 防止注入其他破坏性的语句
            if (!fieldInfo.isStr) {
                for (String str : Constants.SQL_DISASTROUS_KEY) {
                    if (fieldInfo.value.toString().contains(str)) {
                        log.error("field : {} contains  disastrous key : {}", fieldInfo, str);
                        throw new OvineHerdException(ErrorCode.SYSTEM_ERROR);
                    }
                }
            }
        }
    }


    private UpsertDataModel getUpsertDataModel(Long modelId, List<FieldApiResponse> fieldApiResponses) {
        UpsertDataModel dataModel = new UpsertDataModel();
        DatabaseSchema databaseSchema = getDatabaseSchema(modelId);
        dataModel.setDatabaseName(databaseSchema.getDatabaseName());
        dataModel.setTableName(databaseSchema.getTableName());
        List<UpsertDataModel.FieldInfo> fieldInfoList = fieldApiResponses.stream().map(fieldApiResponse -> {
            RelFieldInfo relFieldInfo = new RelFieldInfo(fieldApiResponse.getDbName(), fieldApiResponse.getIsStr());
            UpsertDataModel.FieldInfo fieldInfo = new UpsertDataModel.FieldInfo();
            fieldInfo.setFieldName(relFieldInfo.getFieldName());
            fieldInfo.setStr(relFieldInfo.isStr());
            fieldInfo.setValue(fieldApiResponse.getValue());
            return fieldInfo;
        }).collect(Collectors.toList());
        dataModel.setFieldInfoList(fieldInfoList);
        return dataModel;
    }

    @Data
    @AllArgsConstructor
    public static class RelFieldInfo {
        private String fieldName;
        private boolean isStr;
    }

    private RelFieldInfo getFieldInfoByFieldMap(Map<String, DataModelAttributeEntity> fieldMap, String fieldId) {
        List<String> baseKeyList = SqlFieldKey.baseFieldNameList();
        if (FieldConstant.STORE_MAP.keySet().contains(fieldId)) {
            fieldId = FieldConstant.STORE_MAP.get(fieldId);
        }
        String[] nonStrFields = {SqlFieldKey.ISDEL.getKey(), SqlFieldKey.ID.getKey()};
        if (baseKeyList.contains(fieldId)) {
            if (ArrayUtils.contains(nonStrFields, fieldId)) {
                return new RelFieldInfo(fieldId, false);
            } else {
                return new RelFieldInfo(fieldId, true);
            }
        }
        DataModelAttributeEntity entity = fieldMap.get(fieldId);
        Map<String, BeanTypeKey> enumMap = EnumUtil.getEnumMap(BeanTypeKey.class);
        return new RelFieldInfo(entity.getDbName(), beanTypeIsStr(enumMap.get(entity.getBeanType())));
    }

    /**
     * @param modelId
     * @return key: fieldId
     */
    private Map<String, DataModelAttributeEntity> getFieldMapByModelId(Long modelId) {
        QueryWrapper<DataModelAttributeEntity> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(DataModelAttributeEntity::getTableId, modelId);
        return modelAttributeService.list(wrapper).stream().collect(Collectors.toMap(ele -> ele.getId().toString(), entity -> entity));
    }

    private boolean beanTypeIsStr(BeanTypeKey beanTypeKey) {
        Requires.requireNonNull(beanTypeKey, "bean type is null");
        final BeanTypeKey[] nonStrBeanTypes = {BeanTypeKey.SWITCH, BeanTypeKey.AGE, BeanTypeKey.MONEY, BeanTypeKey.NUMBER};
        return !ArrayUtils.contains(nonStrBeanTypes, beanTypeKey);
    }

    private String getFieldValue(UpsertDataModel.FieldInfo fieldInfo) {
        Requires.requireNonNull(fieldInfo, "field info is null");
        if (fieldInfo.isStr) {
            return SQLUtils.strWarp(fieldInfo.value.toString());
        } else {
            return fieldInfo.value.toString();
        }
    }


    /**
     * 真实访问的数据模型
     */
    @Data
    static class UpsertDataModel {
        private String databaseName;
        private String tableName;
        private List<FieldInfo> fieldInfoList;

        @Data
        static class FieldInfo {
            private String fieldName;
            private Object value;
            private boolean isStr; //  当前字段是否是字符串形式的，比如： 时间类型，字符串类型
        }

    }

    /**
     * TODO 需要结合数据模型一起使用
     *
     * @param modelId 模型id
     */
    private List<FieldApiResponse> checkArg(Long modelId, ModelFieldRequest modelFieldRequest, Long userId) {
        List<Long> ids = modelFieldRequest.getFields().stream().map(ModelFieldRequest.FieldDescribe::getId).collect(Collectors.toList());
        Long versionId = dataModelVersionService.getModelVersionPublish(userId);
        if(versionId == null){
            throw new ModelException("请先进行发布模型和关系");
        }
        QueryWrapper<DataModelAttributeEntity> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(DataModelAttributeEntity::getTableId, modelId)
                .in(DataModelAttributeEntity::getId, ids)
                .eq(DataModelAttributeEntity::getVersionId, versionId);
        List<DataModelAttributeEntity> dataModelAttributeEntities = modelAttributeService.list(wrapper);
        //返回值
        List<FieldApiResponse> checkResponse = new ArrayList<>();
        for (DataModelAttributeEntity dataModelAttributeEntity :
                dataModelAttributeEntities) {
            boolean contain = false;
            for (ModelFieldRequest.FieldDescribe request : modelFieldRequest.getFields()) {
                if (dataModelAttributeEntity.getId() == request.getId()) {
                    contain = true;
                    BasicAttribute basicAttribute = getByBeanType(dataModelAttributeEntity.getBeanType());
                    checkResponse.addAll(basicAttribute.verifyRequestData(dataModelAttributeEntity, request.getValue()));
                }
            }
            if (!contain) {
                throw new ModelException("缺少参数，" + dataModelAttributeEntity.getName());
            }
        }
        return checkResponse;
    }

    private BasicAttribute getByBeanType(String beanType) {
        Requires.requireNonBlank(beanType, "bean type is null");
        Map<String, BeanTypeKey> enumMap = EnumUtil.getEnumMap(BeanTypeKey.class);
        Class<?> classes = null;
        try {
            classes = Class.forName(enumMap.get(beanType.toUpperCase()).getBean());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            throw new ModelException("beanType is null");
        }
        return (BasicAttribute) ReflectUtil.newInstance(classes);
    }

    private DataModelTableEntity checkModelId(Long modelId) {
        DataModelTableEntity modelTableEntity = modelTableService.getById(modelId);
        Requires.requireNonNull(modelTableEntity, String.format("modelId : %s not found ", modelId));
        return modelTableEntity;
    }
}
